#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/resource.h>
//#include "../common/log.hpp"
#include "../RPC/source/common/log.hpp"
#include "../common/util.hpp"

namespace oj_runner
{
    using namespace oj_util;
    //using namespace oj_log;

    /*
    *   该模块只负责对代码的运行，代码运行的结果正确与否，该模块不关心
    *   该模块只关心代码运行时是否发生异常
    */
    class Runner
    {
    private:
        static void setCpuMemLimit(int cpu_limit, int mem_limit)
        {
            struct rlimit cpu;
            cpu.rlim_cur = cpu_limit;
            cpu.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU, &cpu);

            struct rlimit mem;
            mem.rlim_cur = mem_limit * 1024;    //转换成kb
            mem.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS, &mem);
        }
    public:
        /*
         *   返回值大于0，说明运行时发生异常，返回值就是收到的异常信号的编号
         *   返回值等于0，说明运行正确，得到结果
         *   返回值小于0，说明内部错误
         * 
         *   file_name: 传入文件名即可，不需要带路径、后缀
         *   cpu_limit: 程序运行的时候，CPU使用时间限制（s）
         *   mme_limit: 程序运行的时候，虚拟内存占用显示（kb）
         */
        static int run(const std::string &file_name, int cpu_limit, int mem_limit)
        {
            const std::string execute = PathUtil::Exe(file_name);
            const std::string stdin_file = PathUtil::Stdin(file_name);
            const std::string stdout_file = PathUtil::Stdout(file_name);
            const std::string stderr_file = PathUtil::Stderr(file_name);

            umask(0);
            int stdin_fd = open(stdin_file.c_str(), O_CREAT | O_RDONLY, 0644);
            int stdout_fd = open(stdout_file.c_str(), O_CREAT | O_WRONLY, 0644);
            int stderr_fd = open(stderr_file.c_str(), O_CREAT | O_WRONLY, 0644);

            if (stdin_fd < 0 || stdout_fd < 0 || stderr_fd < 0)
            {
                LOG(JsonMuduoRPC::ERROR, "运行时，标准文件打开失败");
                return -1;
            }

            pid_t pid = fork();
            if (pid < 0)
            {
                LOG(JsonMuduoRPC::FATAL, "运行时，进程创建失败");
                return -2;
            }
            if (pid == 0)
            {
                // child

                setCpuMemLimit(cpu_limit, mem_limit);

                //重定向标准输入、输出、错误
                dup2(stdin_fd, 0);
                dup2(stdout_fd, 1);
                dup2(stderr_fd, 2);

                //执行哪个方法  怎么执行
                execl(execute.c_str(), execute.c_str(), nullptr);

                LOG(JsonMuduoRPC::WARNING, "%s 程序运行失败", execute.c_str());
                exit(1);
            }
            // father
            close(stdin_fd);
            close(stdout_fd);
            close(stderr_fd);

            int status = 0;
            waitpid(pid, &status, 0);
            LOG(JsonMuduoRPC::INFO, "运行完毕, info: %d", status & 0x7f);
            return status & 0x7f;
        }
    };
}